package com.zifan.yygh.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.zifan.common.result.ResultCodeEnum;
import com.zifan.yygh.exception.YyghException;
import com.zifan.yygh.model.hosp.Schedule;
import com.zifan.yygh.repository.ScheduleRepository;
import com.zifan.yygh.service.DepartmentService;
import com.zifan.yygh.service.HospitalService;
import com.zifan.yygh.service.ScheduleService;
import com.zifan.yygh.vo.hosp.BookingScheduleRuleVo;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Zifan
 * @date 2021年09月28日15:33
 */
@Service
@SuppressWarnings("all")
public class ScheduleServiceImpl implements ScheduleService {
    @Autowired
    private ScheduleRepository scheduleRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private HospitalService hospitalService;

    @Autowired
    private DepartmentService departmentService;


    /**
     * 保存排版信息
     */
    @Override
    public void saveSchedule(Map<String, Object> paramMap) {
        //todo 签名校验

        //转换对象
        Schedule schedule = JSON.parseObject(JSON.toJSONString(paramMap), Schedule.class);
        String hoscode = schedule.getHoscode();
        String scheduleId = schedule.getHosScheduleId();

        //根据医院编号和排班编号获取排班信息
        Schedule resSchedule = scheduleRepository.getScheduleByHoscodeAndHosScheduleId(hoscode, scheduleId);
        if (ObjectUtil.isNull(resSchedule)) {
            //新增
            schedule.setCreateTime(new Date());
            schedule.setUpdateTime(new Date());
            schedule.setStatus(1);
            schedule.setIsDeleted(0);
        } else {
            //维护
            schedule.setId(resSchedule.getId());
            schedule.setUpdateTime(new Date());
            schedule.setStatus(1);
            schedule.setIsDeleted(0);
        }
        scheduleRepository.save(schedule);
    }

    /**
     * 获取排班信息，分页、条件、排序
     */
    @Override
    public Page<Schedule> getScheduleList(Map<String, Object> paramMap) {
        //todo 签名校验

        //接收参数
        String hoscode = (String) paramMap.get("hoscode");
        if (StrUtil.isEmpty(hoscode)) {
            throw new YyghException(ResultCodeEnum.PARAM_ERROR);
        }
        String depcode = (String) paramMap.get("depcode");
        //获取分页信息
        Integer page = StringUtils.isEmpty(paramMap.get("page")) ? 1 : Integer.parseInt((String) paramMap.get("page"));
        Integer limit = StringUtils.isEmpty(paramMap.get("limit")) ? 10 : Integer.parseInt((String) paramMap.get("limit"));

        //创建条件匹配器
        ExampleMatcher exampleMatcher = ExampleMatcher.matching()//构建对象
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)//检索条件：模糊查询
                .withIgnoreCase(true);//大小写忽略
        Schedule schedule = new Schedule();
        schedule.setHoscode(hoscode);
        schedule.setDepcode(depcode);
        Example<Schedule> example = Example.of(schedule, exampleMatcher);

        //构建排序
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");

        //构建分页信息
        Pageable pageable = PageRequest.of(page - 1, limit, sort);

        //查询信息并返回
        return scheduleRepository.findAll(example, pageable);
    }

    /**
     * 删除排班信息
     */
    @Override
    public void removeSchedule(Map<String, Object> paramMap) {
        String hoscode = (String) paramMap.get("hoscode");
        if (StringUtils.isEmpty(hoscode)) {
            throw new YyghException(ResultCodeEnum.PARAM_ERROR);
        }
        String hosScheduleId = (String) paramMap.get("hosScheduleId");
        Schedule schedule = scheduleRepository.getScheduleByHoscodeAndHosScheduleId(hoscode, hosScheduleId);
        if (ObjectUtil.isNotNull(schedule)) {
            scheduleRepository.deleteById(schedule.getId());
        } else {
            throw new YyghException(ResultCodeEnum.PARAM_ERROR);
        }

    }

    /**
     * 根据医院编号 和 科室编号 ，查询排班规则数据
     */
    @Override
    public Map<String, Object> getRuleSchedule(long page, long limit, String hoscode, String depcode) {

        //这里其实很要实现的功能挺简单的，就是mongoDB的聚合操作相关的API不好记，所以体现出来很麻烦

        //1条件查询 根据医院编号和科室编号 拼接条件
        Criteria criteria = Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode);

        //2聚合操作 注意不要导错包

        /**
         * 这个聚合相关的操作看这个贴子，很详细 https://blog.csdn.net/qq_35720307/article/details/80930057
         */

        Aggregation agg = Aggregation.newAggregation(
                //参数1-匹配的条件,就是上面的写哪个条件查询
                Aggregation.match(criteria),

                //参数2-聚合的具体操作
                //分组相关，根据哪个字段进行分局 workDate-工作日期
                Aggregation.group("workDate").first("workDate").as("workDate")
                        //分组后开始求和，这里求的是医生的总数，逻辑是这样的，按日期分组之后。这个分组下有几条数据就代表这这天有几个医生
                        .count().as("docCount")
                        //这里的意思是分组后，把当前分组下所有的reservedNumber（可预约人数）相加的出来的数据，在这就是27+40+33=100
                        .sum("reservedNumber").as("reservedNumber")
                        //同理，这里的意思是分组后，把当前分组下所有的availableNumber（剩余人数）相加，这里其中一个是10+6+22=38
                        .sum("availableNumber").as("availableNumber"),

                //参数3-排序相关 排序方式 根据workDate升序
                Aggregation.sort(Sort.Direction.DESC, "workDate"),

                //参数4-实现分页 当前页
                Aggregation.skip((page - 1) * limit),
                //参数5-实现分页 每页数量
                Aggregation.limit(limit)
        );

        //3调用模板方法，获取数据
        AggregationResults<BookingScheduleRuleVo> aggregate = mongoTemplate.aggregate(agg, Schedule.class, BookingScheduleRuleVo.class);
        List<BookingScheduleRuleVo> mappedResults = aggregate.getMappedResults();//最终结果

        //4获取其他信息-分页查询总计记录数
        Aggregation totalAgg = Aggregation.newAggregation(
                //1条件
                Aggregation.match(criteria),
                //2分组字段
                Aggregation.group("workDate")
        );
        AggregationResults<BookingScheduleRuleVo> totalRes = mongoTemplate.aggregate(totalAgg, Schedule.class, BookingScheduleRuleVo.class);
        int total = totalRes.getMappedResults().size();

        //5封装数据 日期对应的星期
        mappedResults.forEach(bookingScheduleRuleVo->{
            Date workDate = bookingScheduleRuleVo.getWorkDate();
            String week = this.getDayOfWeek(new DateTime(workDate));
            bookingScheduleRuleVo.setDayOfWeek(week);
        });

        //6封装返回数据
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("bookingScheduleRuleList",mappedResults);
        resMap.put("total",total);
        //获取医院名称
        String hosName = hospitalService.getHospName(hoscode);
        //其他基础数据 你看这里就挺tm逆天的，一个hosName非得包一个map。
        Map<String, String> baseMap = new HashMap<>();
        baseMap.put("hosname",hosName);
        resMap.put("baseMap",baseMap);

        return resMap;
    }

    /**
     * 根据医院编号 、科室编号和工作日期，查询排班详细信息
     */
    @Override
    public List<Schedule> getDetailSchedule(String hoscode, String depcode, String workDate) {
        List<Schedule> schedules = scheduleRepository.findScheduleByHoscodeAndDepcodeAndWorkDate(
                hoscode, depcode, new DateTime(workDate).toDate()
        );
        //封装一些其他信息
        schedules.forEach(item->{
            this.packageSchedule(item);
        });

        return schedules;
    }

    private void packageSchedule(Schedule schedule) {
        //设置医院名称
        schedule.getParam().put("hosname",hospitalService.getHospName(schedule.getHoscode()));
        //设置科室名称
        schedule.getParam().put("depname",departmentService.getDepName(schedule.getHoscode(),schedule.getDepcode()));
        //设置日期对应星期
        schedule.getParam().put("dayOfWeek",this.getDayOfWeek(new DateTime(schedule.getWorkDate())));
    }

    /**
     * 根据日期获取周几数据
     * @param dateTime
     * @return
     */
    private String getDayOfWeek(DateTime dateTime) {
        String dayOfWeek = "";
        switch (dateTime.getDayOfWeek()) {
            case DateTimeConstants.SUNDAY:
                dayOfWeek = "周日";
                break;
            case DateTimeConstants.MONDAY:
                dayOfWeek = "周一";
                break;
            case DateTimeConstants.TUESDAY:
                dayOfWeek = "周二";
                break;
            case DateTimeConstants.WEDNESDAY:
                dayOfWeek = "周三";
                break;
            case DateTimeConstants.THURSDAY:
                dayOfWeek = "周四";
                break;
            case DateTimeConstants.FRIDAY:
                dayOfWeek = "周五";
                break;
            case DateTimeConstants.SATURDAY:
                dayOfWeek = "周六";
            default:
                break;
        }
        return dayOfWeek;
    }

}
